#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdlib.h>

#define MAX 1024
#define ARGC 64
#define SEP " "

enum redir
{
    INPUT = 0,
    OUTPUT,
    APPEND,
    NONE
};

bool split(char *s, char *argv[])
{
    assert(s);
    assert(argv);
    argv[0] = strtok(s, SEP);
    if (argv[0] == NULL)
        return false;
    int i = 1;
    while (1)
    {
        argv[i] = strtok(NULL, SEP);
        if (argv[i] == NULL)
            break;
        i++;
    }

    return true;
}

void debugprint(char *argv[])
{
    for (int i = 0; argv[i]; i++)
    {
        printf("%d: %s\n", i, argv[i]);
    }
}

void showenv()
{
    extern char **environ;
    for (int i = 0; environ[i]; i++)
    {
        printf("%d:%s\n", i, environ[i]);
    }
}

char *checkdir(char *str, enum redir *redir_type)
{
    int i = 0;
    while (str[i] != '\0')
    {
        if (str[i] == '>' || str[i] == '<')
        {
            if (str[i] == '>')
            {
                if (str[i + 1] == '>')
                {
                    str[i] = str[i + 1] = '\0';
                    *redir_type = APPEND;
                    if (str[i + 2] == ' ')
                        return str + i + 3;
                    return str + i + 2;
                }
                else
                {
                    str[i] = '\0';
                    *redir_type = OUTPUT;
                    if (str[i + 1] == ' ')
                        return str + i + 2;
                    return str + i + 1;
                }
            }
            else
            {
                str[i] = '\0';
                *redir_type = INPUT;
                if (str[i + 1] == ' ')
                    return str + i + 2;
                return str + i + 1;
            }
        }
        i++;
    }
    return NULL;
}

int main()
{
    int exit_code = 0;
    char myenv[ARGC][256]; // 维护一个自己导入的环境变量表
    int myenv_index = 0;
    while (1)
    {
        enum redir redir_type = NONE;
        char inputstring[MAX] = {0};
        char *argv[ARGC] = {NULL};
        printf("[Alpaca@mymachine curpath]$"); // 命令行打印
        fflush(stdout);
        char *s = fgets(inputstring, sizeof(inputstring), stdin); // 获取命令
        assert(s);
        inputstring[strlen(inputstring) - 1] = '\0';

        char *file = checkdir(inputstring, &redir_type); // 判断是否有重定向
        if (!split(inputstring, argv))
            continue; // 分隔命令
        // debugprint(argv);

        if (strcmp(argv[0], "ls") == 0)
        {
            int i = 1;
            while (argv[i] != NULL)
                i++;
            argv[i++] = (char *)"--color=auto";
            argv[i] = NULL;
        }
        if (strcmp(argv[0], "cd") == 0) // 内建命令需要bash自己调用系统对应的函数
        {
            if (argv[1] != NULL)
                chdir(argv[1]);
            continue;
        }
        if (strcmp(argv[0], "echo") == 0)
        {
            const char *target = NULL;
            if (argv[1][0] == '$')
            {
                if (argv[1][1] == '?')
                {
                    printf("%d\n", exit_code);
                    continue;
                }
                else
                    target = getenv(argv[1] + 1);
                if (target != NULL)
                    printf("%s=%s", argv[1] + 1, target);
                continue;
            }
        }
        if (strcmp(argv[0], "export") == 0)
        {
            if (argv[1] != NULL)
            {
                strcpy(myenv[myenv_index++], argv[1]);
                putenv(myenv);
            }
            continue;
        }
        if (strcmp(argv[0], "env") == 0)
        {
            showenv();
            continue;
        }

        int pid = fork(); // 创建子进程
        assert(pid >= 0);
        if (pid == 0) // 子进程
        {
            if (redir_type != NONE) // 命令中有重定向操作
            {
                int fd;
                if (redir_type == INPUT)
                {
                    fd = open(file, O_RDONLY);
                    dup2(fd, 0);
                }
                else
                {
                    if (redir_type == OUTPUT)
                    {
                        fd = open(file, O_WRONLY | O_CREAT | O_TRUNC);
                    }
                    else
                    {
                        fd = open(file, O_WRONLY | O_CREAT | O_APPEND);
                    }
                    dup2(fd, 1);
                }
            }
            execvp(argv[0], argv);
            exit(1);
        }
        int status;
        pid_t ret = waitpid(pid, &status, 0);
        if (ret > 0)
        {
            exit_code = WEXITSTATUS(status);
        }
    }
    return 0;
}